package com.schedule.utils;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.schedule.dto.AutoScheduleDto;
import com.schedule.entity.*;
import com.schedule.mapper.*;
import com.schedule.vo.ScheduleResultVo;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 排课算法核心实现
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class ScheduleAlgorithm {

    @Resource
    private CourseMapper courseMapper;
    @Resource
    private TeacherMapper teacherMapper;
    @Resource
    private ScheduleMapper scheduleMapper;
    @Resource
    private HolidayMapper holidayMapper;
    @Resource
    private TeacherCourseMapper teacherCourseMapper;

    /**
     * 执行排课算法
     */
    public ScheduleResultVo execute(AutoScheduleDto request, List<Course> leafCourses) {
        log.info("开始执行排课算法，课程数量：{}", leafCourses.size());
        
        ScheduleResultVo result = new ScheduleResultVo();
        result.setClassId(request.getClassId());
        result.setRootCourseId(request.getRootCourseId());
        result.setStartDate(request.getStartDate());
        
        try {
            // 1. 计算总课时
            int totalHours = leafCourses.stream().mapToInt(Course::getHours).sum();
            result.setTotalHours(totalHours);
            
            // 2. 获取可用教师和课程的映射关系
            Map<Long, List<Long>> courseTeacherMap = buildCourseTeacherMap(leafCourses, request.getTeacherIds());
            
            // 3. 初始化教师工作量计数器
            Map<Long, Integer> teacherHourCount = new HashMap<>();
            request.getTeacherIds().forEach(teacherId -> teacherHourCount.put(teacherId, 0));
            
            // 4. 执行排课
            List<ScheduleResultVo.ScheduleItemVo> scheduleItems = new ArrayList<>();
            LocalDate currentDate = request.getStartDate();
            int currentTimeSlot = request.getStartTimeSlot();
            
            for (Course course : leafCourses) {
                List<ScheduleResultVo.ScheduleItemVo> courseSchedules = scheduleCourse(
                    course, request, courseTeacherMap, teacherHourCount, currentDate, currentTimeSlot);
                
                scheduleItems.addAll(courseSchedules);
                
                // 更新当前日期和时间段
                if (!courseSchedules.isEmpty()) {
                    ScheduleResultVo.ScheduleItemVo lastItem = courseSchedules.get(courseSchedules.size() - 1);
                    currentDate = lastItem.getScheduleDate();
                    currentTimeSlot = lastItem.getTimeSlot() + 1;
                    
                    // 如果超过一天的最后一个时间段，跳到下一天
                    if (currentTimeSlot > 8) {
                        currentDate = getNextAvailableDate(currentDate, request.getSkipHolidays());
                        currentTimeSlot = 1;
                    }
                }
            }
            
            result.setScheduleItems(scheduleItems);
            result.setEndDate(scheduleItems.isEmpty() ? currentDate : 
                scheduleItems.get(scheduleItems.size() - 1).getScheduleDate());
            result.setSuccess(true);
            result.setMessage("排课成功");
            
        } catch (Exception e) {
            log.error("排课算法执行失败", e);
            result.setSuccess(false);
            result.setMessage("排课失败：" + e.getMessage());
        }
        
        return result;
    }

    /**
     * 为单个课程排课
     */
    private List<ScheduleResultVo.ScheduleItemVo> scheduleCourse(
            Course course, AutoScheduleDto request, Map<Long, List<Long>> courseTeacherMap,
            Map<Long, Integer> teacherHourCount, LocalDate startDate, int startTimeSlot) {
        
        List<ScheduleResultVo.ScheduleItemVo> result = new ArrayList<>();
        List<Long> availableTeachers = courseTeacherMap.get(course.getId());
        
        if (availableTeachers == null || availableTeachers.isEmpty()) {
            throw new RuntimeException("课程 " + course.getName() + " 没有可用的授课教师");
        }
        
        int remainingHours = course.getHours();
        LocalDate currentDate = startDate;
        int currentTimeSlot = startTimeSlot;
        
        while (remainingHours > 0) {
            // 选择最合适的教师（工作量最少的）
            Long selectedTeacher = selectBestTeacher(availableTeachers, teacherHourCount, currentDate, currentTimeSlot);
            
            if (selectedTeacher == null) {
                // 没有可用教师，跳到下一个时间段
                currentTimeSlot++;
                if (currentTimeSlot > 8) {
                    currentDate = getNextAvailableDate(currentDate, request.getSkipHolidays());
                    currentTimeSlot = 1;
                }
                continue;
            }
            
            // 检查教师每周工作时间限制
            if (getTeacherWeeklyHours(selectedTeacher, currentDate) >= 40) {
                // 该教师本周工作时间已满，跳到下周
                currentDate = getNextWeekStartDate(currentDate);
                currentTimeSlot = 1;
                continue;
            }
            
            // 创建排课项
            ScheduleResultVo.ScheduleItemVo item = new ScheduleResultVo.ScheduleItemVo();
            item.setScheduleDate(currentDate);
            item.setTimeSlot(currentTimeSlot);
            item.setCourseId(course.getId());
            item.setCourseName(course.getName());
            item.setTeacherId(selectedTeacher);
            item.setTeacherName(getTeacherName(selectedTeacher));
            item.setVenueId(request.getVenueId());
            item.setDuration(1); // 每次排1个课时
            item.setWeekDay(currentDate.getDayOfWeek().toString());
            item.setStatus("scheduled");
            
            result.add(item);
            
            // 更新计数器
            teacherHourCount.put(selectedTeacher, teacherHourCount.get(selectedTeacher) + 1);
            remainingHours--;
            
            // 移动到下一个时间段
            currentTimeSlot++;
            if (currentTimeSlot > 8) {
                currentDate = getNextAvailableDate(currentDate, request.getSkipHolidays());
                currentTimeSlot = 1;
            }
        }
        
        return result;
    }

    /**
     * 选择最合适的教师
     */
    private Long selectBestTeacher(List<Long> availableTeachers, Map<Long, Integer> teacherHourCount, 
                                  LocalDate date, int timeSlot) {
        
        Long bestTeacher = null;
        int minHours = Integer.MAX_VALUE;
        
        for (Long teacherId : availableTeachers) {
            // 检查教师是否有时间冲突
            if (hasTeacherConflict(teacherId, date, timeSlot)) {
                continue;
            }
            
            // 选择工作量最少的教师
            int currentHours = teacherHourCount.get(teacherId);
            if (currentHours < minHours) {
                minHours = currentHours;
                bestTeacher = teacherId;
            }
        }
        
        return bestTeacher;
    }

    /**
     * 检查教师时间冲突
     */
    private boolean hasTeacherConflict(Long teacherId, LocalDate date, int timeSlot) {
        LambdaQueryWrapper<Schedule> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Schedule::getTeacherId, teacherId)
                .eq(Schedule::getScheduleDate, date)
                .eq(Schedule::getTimeSlot, timeSlot);
        
        return scheduleMapper.selectCount(wrapper) > 0;
    }

    /**
     * 获取教师本周已排课时数
     */
    private int getTeacherWeeklyHours(Long teacherId, LocalDate date) {
        LocalDate weekStart = date.with(DayOfWeek.MONDAY);
        LocalDate weekEnd = date.with(DayOfWeek.SUNDAY);
        
        LambdaQueryWrapper<Schedule> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Schedule::getTeacherId, teacherId)
                .between(Schedule::getScheduleDate, weekStart, weekEnd);
        
        List<Schedule> schedules = scheduleMapper.selectList(wrapper);
        return schedules.stream().mapToInt(Schedule::getDuration).sum();
    }

    /**
     * 获取下一个可用日期（跳过节假日和周日）
     */
    private LocalDate getNextAvailableDate(LocalDate date, Boolean skipHolidays) {
        LocalDate nextDate = date.plusDays(1);
        
        // 跳过周日
        if (nextDate.getDayOfWeek() == DayOfWeek.SUNDAY) {
            nextDate = nextDate.plusDays(1);
        }
        
        // 跳过节假日
        if (skipHolidays && isHoliday(nextDate)) {
            return getNextAvailableDate(nextDate, skipHolidays);
        }
        
        return nextDate;
    }

    /**
     * 获取下周开始日期
     */
    private LocalDate getNextWeekStartDate(LocalDate date) {
        return date.with(DayOfWeek.MONDAY).plusWeeks(1);
    }

    /**
     * 检查是否为节假日
     */
    private boolean isHoliday(LocalDate date) {
        LambdaQueryWrapper<Holiday> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Holiday::getHolidayDate, date)
                .eq(Holiday::getType, "holiday");
        
        return holidayMapper.selectCount(wrapper) > 0;
    }

    /**
     * 构建课程-教师映射关系
     */
    private Map<Long, List<Long>> buildCourseTeacherMap(List<Course> courses, List<Long> teacherIds) {
        Map<Long, List<Long>> courseTeacherMap = new HashMap<>();
        
        for (Course course : courses) {
            List<Long> authorizedTeachers = new ArrayList<>();
            
            for (Long teacherId : teacherIds) {
                LambdaQueryWrapper<TeacherCourse> wrapper = new LambdaQueryWrapper<>();
                wrapper.eq(TeacherCourse::getTeacherId, teacherId)
                        .eq(TeacherCourse::getCourseId, course.getId());
                
                if (teacherCourseMapper.selectCount(wrapper) > 0) {
                    authorizedTeachers.add(teacherId);
                }
            }
            
            courseTeacherMap.put(course.getId(), authorizedTeachers);
        }
        
        return courseTeacherMap;
    }

    /**
     * 获取所有叶子课程
     */
    public List<Course> getLeafCourses(Long rootCourseId) {
        List<Course> allCourses = new ArrayList<>();
        collectLeafCourses(rootCourseId, allCourses);
        return allCourses;
    }

    private void collectLeafCourses(Long parentId, List<Course> result) {
        LambdaQueryWrapper<Course> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Course::getParentId, parentId)
                .eq(Course::getStatus, 1)
                .orderByAsc(Course::getSortOrder);
        
        List<Course> children = courseMapper.selectList(wrapper);
        
        if (children.isEmpty()) {
            // 这是叶子节点
            Course course = courseMapper.selectById(parentId);
            if (course != null && course.getHours() > 0) {
                result.add(course);
            }
        } else {
            // 递归处理子节点
            for (Course child : children) {
                collectLeafCourses(child.getId(), result);
            }
        }
    }

    /**
     * 检查排课冲突
     */
    public List<String> checkConflicts(AutoScheduleDto request) {
        List<String> conflicts = new ArrayList<>();
        
        // 检查教师授权
        List<Course> leafCourses = getLeafCourses(request.getRootCourseId());
        for (Course course : leafCourses) {
            boolean hasAuthorizedTeacher = false;
            for (Long teacherId : request.getTeacherIds()) {
                LambdaQueryWrapper<TeacherCourse> wrapper = new LambdaQueryWrapper<>();
                wrapper.eq(TeacherCourse::getTeacherId, teacherId)
                        .eq(TeacherCourse::getCourseId, course.getId());
                
                if (teacherCourseMapper.selectCount(wrapper) > 0) {
                    hasAuthorizedTeacher = true;
                    break;
                }
            }
            
            if (!hasAuthorizedTeacher) {
                conflicts.add("课程 " + course.getName() + " 没有授权的教师");
            }
        }
        
        return conflicts;
    }

    private String getTeacherName(Long teacherId) {
        Teacher teacher = teacherMapper.selectById(teacherId);
        return teacher != null ? teacher.getName() : "未知教师";
    }
}